//#include <GL/glut.h>
#include <GL/freeglut.h>
#include <math.h>

#include <stdlib.h>
#include <stdio.h>

static float angle = 0.00001f, ratio;
static float x = 0.0f, y = -5.0f, z = -95.0f;
static float lx = 0.0f, ly = 0.0f, lz = 1.0f;
static GLint xMouse = 0, yMouse = 0, mouseButton = -1, mouseAction = -1;
//static int sequence[125][3] = {
//								 { 1, 1, 1 }, { 0, 2, 3 }, { 4, 1, 1 }, { 3, 1, 2 }, { 3, 4, 1 }, { 3, 2, 2 },
//								 { 2, 2, 2 }, { 2, 1, 2 }, { 2, 2, 2 }, { 2, 2, 1 }, { 1, 2, 2 }, { 3, 2, 2 },
//								 { 2, 3, 2 }, { 2, 2, 3 }, { 4, 2, 2 }, { 2, 4, 2 }, { 2, 2, 4 }, { 0, 2, 2 },
//								 { 2, 0, 2 }, { 1, 0, 2 }, { 3, 0, 2 }, { 4, 0, 2 }, { 2, 0, 1 }, { 2, 0, 3 },
//								 { 2, 0, 4 }, { 2, 1, 2 }, { 2, 2, 2 }, { 2, 3, 2 }, { 0, 0, 2 }, { 2, 0, 0 },
//								 { 2, 4, 2 }, { 2, 3, 2 }, { 2, 1, 2 }, { 1, 0, 1 }, { 3, 0, 3 }, { 4, 0, 4 },
//								 { 0, 0, 0 }, { 4, 0, 1 }, { 4, 0, 3 }, { 4, 0, 0 }, { 2, 3, 3 }, { 2, 4, 4 },
//								 { 2, 1, 1 }, { 1, 1, 2 }, { 3, 3, 2 }, { 4, 4, 2 }, { 3, 3, 3 }, { 3, 1, 1 },
//								 { 3, 2, 2 }, { 3, 4, 4 }, { 3, 0, 0 }, { 2, 0, 2 }, { 2, 0, 2 }, { 2, 0, 2 } };

void changeSize(int w, int h)
{
	// Prevent a divide by zero, when window is too short
	// (you cant make a window of zero width).
	if (h == 0) h = 1;

	ratio = 1.0f * w / h;
	// Reset the coordinate system before modifying
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	// Set the viewport to be the entire window
	glViewport(0, 0, w, h);

	// Set the clipping volume
	gluPerspective(45, ratio, 1, 1500);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
}

void renderScene(void)
{
	const double t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
	//	const int index = ((int) floor(t)) % 50;

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glColor3d(0.2, 0.8, 0.9);

	for (int i = -2; i <= 2; i++)
		for (int j = -2; j <= 2; j++)
		{
			glBegin(GL_LINES);
			glVertex3f(i * 10.0f, j * 10.0f, 0.0f);
			glVertex3f(i * 10.f, j * 10.0f, 40.0f);
			glEnd();
		}

	for (int i = -2; i <= 2; i++)
		for (int j = -2; j <= 2; j++)
		{
			glBegin(GL_LINES);
			glVertex3f(i * 10.0f, -20.0f, (j + 2) * 10.0f);
			glVertex3f(i * 10.f, 20.0f, (j + 2) * 10.0f);
			glEnd();
		}

	for (int i = -2; i <= 2; i++)
		for (int j = -2; j <= 2; j++)
		{
			glBegin(GL_LINES);
			glVertex3f(-20.0f, i * 10.0f, (j + 2) * 10.0f);
			glVertex3f(20.0f, i * 10.f, (j + 2) * 10.0f);
			glEnd();
		}

	for (int i = -2; i <= 2; i++)
		for (int j = -2; j <= 2; j++)
			for (int k = 0; k <= 4; k++)
			{
				glPushMatrix();
				glTranslatef(i * 10.0f, j * 10.0f, k * 10.0f);
				glutSolidSphere(0.5, 20, 20);
				glPopMatrix();
			}

	//	for (int i = 0; i <= index; i++)
	//	{
	//		if (i % 2 == 0) glColor3d(1.0, 1.0, 1.0);
	//		else glColor3d(0.5, 0.1, 0.0);
	//
	//		glPushMatrix();
	//		glTranslatef((sequence[i][0] - 2) * 10.0f, (sequence[i][1] - 2) * 10.0f, sequence[i][2] * 10.0f);
	//		glutSolidSphere(2.5, 20, 20);
	//		glPopMatrix();
	//	}

	glutSwapBuffers();
}

void orientMe(float ang)
{
	lx = sin(ang);
	lz = cos(ang);
	glLoadIdentity();
	gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
}

void moveMeFlat(int i)
{
	x = x + i * (lx) * 0.5;
	z = z + i * (lz) * 0.5;
	glLoadIdentity();
	gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
}

void processNormalKeys(unsigned char key, int x, int y)
{
	if (key == 27) exit(0);
}

void inputKey(int key, int x, int y)
{
	switch (key)
	{
		case GLUT_KEY_LEFT:
			angle -= 0.01f;
			orientMe(angle);
		break;
		case GLUT_KEY_RIGHT:
			angle += 0.01f;
			orientMe(angle);
		break;
		case GLUT_KEY_UP:
			moveMeFlat(1);
		break;
		case GLUT_KEY_DOWN:
			moveMeFlat(-1);
		break;
	}
}

void processMouseAction(GLint button, GLint action, GLint x, GLint y)
{
	// Guardamos el estado de los botones y la posición del ratón
	mouseButton = button;
	mouseAction = action;
	xMouse = x;
	yMouse = y;
}

void processMouseActiveMotion(GLint x, GLint y)
{
	GLint dx = xMouse - x;
	GLint dy = yMouse - y;
	if (mouseAction == GLUT_UP)
	{
		if (mouseButton == 3) moveMeFlat(-1);
		else moveMeFlat(1);
	}
	else if (mouseAction == GLUT_DOWN)
	{
		if (mouseButton == GLUT_LEFT_BUTTON)
		{
			angle -= dx / 1000.0f;
			orientMe(angle);
		}
		if (mouseButton == GLUT_RIGHT_BUTTON)
		{
			if (dy < 0)
			{
				moveMeFlat(-1);
			}
			else if (dy > 0)
			{
				moveMeFlat(1);
			}
		}
		if (mouseButton == 3) moveMeFlat(-1);
		if (mouseButton == 4) moveMeFlat(1);
	}
	xMouse = x;
	yMouse = y;
}

void processMousePassiveMotion(GLint x, GLint y)
{
	xMouse = x;
	yMouse = y;
}

const GLfloat light_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
const GLfloat light_diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat light_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat light_position[] = { 2.0f, 5.0f, 5.0f, 0.0f };

const GLfloat mat_ambient[] = { 0.7f, 0.7f, 0.7f, 1.0f };
const GLfloat mat_diffuse[] = { 0.8f, 0.8f, 0.8f, 1.0f };
const GLfloat mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat high_shininess[] = { 10.0f };

int main(int argc, char **argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowPosition(100, 100);
	glutInitWindowSize(640, 360);
	glutCreateWindow("Go3D");

	glEnable(GL_DEPTH_TEST);

	glutKeyboardFunc(processNormalKeys);
	glutSpecialFunc(inputKey);
	glutMouseFunc(processMouseAction);
	glutMotionFunc(processMouseActiveMotion);
	glutPassiveMotionFunc(processMousePassiveMotion);

	glutDisplayFunc(renderScene);
	glutIdleFunc(renderScene);

	glutReshapeFunc(changeSize);

	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);

	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LESS);

	glEnable(GL_LIGHT0);
	glEnable(GL_NORMALIZE);
	glEnable(GL_COLOR_MATERIAL);
	glEnable(GL_LIGHTING);

	glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
	glLightfv(GL_LIGHT0, GL_POSITION, light_position);

	glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
	glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);

	glutMainLoop();

	return (0);
}
